/**
 *  buf.c - A shared buffer and related calls.
 *
 *  Copyright (C) 2008-2009 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "include/buf.h"


/**
 * bufInit - Create a shared buffer size of 'BufSize'
 * @BufSize: The size of shared buffer.
 * @return: The start address of this shared buffer.
 *
 * @notes:
 */
buf_t *bufInit(uword_t BufSize) {
    buf_t *pbuf = NULL;
    char_t *pdata = NULL;

    pbuf = (buf_t*)kmalloc(sizeof(buf_t));
    if(pbuf == NULL) {
        return(NULL);
    }

    pdata = (char_t *)kmalloc(BufSize *sizeof(char_t));
    if(pdata == NULL) {
        kfree(pbuf); /* it is necessary to free buf_t space
                        while failure to apply for space,
                        and return NULL */
        return(NULL);
    }

    /* initialize shared buffer */
    pbuf->size = BufSize;
    pbuf->used = 0;
    pbuf->start = pdata;
    pbuf->end = &pdata[BufSize - 1];
    pbuf->inpos = pdata;
    pbuf->outpos = pdata;

    /* setting all position of buffer to zero */
    do {
        *pbuf->inpos = 0;
        pbuf->inpos++;
    } while(pbuf->inpos != pbuf->end + 1);
    pbuf->inpos = pdata;
    return(pbuf);
}



/**
 * bufSend - Write a byte of data to the shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @ch: A data which will write to shared buffer.
 * @return: TRUE if sucessfully, otherwise return FALSE.
 *
 * @notes:
 */
bool_t bufSend(buf_t *pBuf, char_t ch) {
    if(pBuf == NULL || (pBuf->start == NULL)) {
        return(FALSE);
    }

    /* Shared buffer is full */
    if(pBuf->size <= pBuf->used) {
        return(FALSE);
    }

    *pBuf->inpos = ch;
    pBuf->used++;
    if(pBuf->inpos++ == pBuf->end) {
        pBuf->inpos = pBuf->start;
    }

    return(TRUE);
}



/**
 * bufSendArray - Write bytes of data to shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @Buf: Pointer to a buffer where data will be write.
 * @return: How many data are written into, otherwise FALSE.
 *
 * @notes: It just called bufSend to complete its job.
 */
uword_t bufSendArray(buf_t *pBuf, const char_t *Buf, word_t Len) {
    const char_t *p = Buf;

    if((pBuf == NULL) || (pBuf->start == NULL) || (Buf == NULL)) {
        return(0);
    }

    while(Len > 0) {
        if(bufSend(pBuf, *p) == TRUE) {
             p++;
        } else {
            return(p - Buf);
        }
        Len--;
    }

    return(p - Buf);
}



/**
 * bufRev - Read a byte from a shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @return: A byte of data if successful, otherwise return 0.
 *
 * @notes:
 */
char_t bufRev(buf_t *pBuf, bool_t *Err) {
    char_t re = 0;

    if((pBuf == NULL) || (pBuf->start == NULL)) {
        Err = FALSE;
        return(re);
    }

    /* shared buffer is empty */
    if(pBuf->used == 0) {
        Err = FALSE;
        return(re);
    }

    re = *pBuf->outpos;
    pBuf->used--;
    if(pBuf->outpos++ == pBuf->end) {
        pBuf->outpos = pBuf->start;
    }

    *Err = TRUE;
    return(re);
}



/**
 * bufRevStr - Read bytes of data from the shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @Buf: Pointer to the buffer that take back data.
 * @return: How many bytes are written.
 *
 * @notes:
 */
uword_t bufRevArray(buf_t *pBuf, char_t *Buf, word_t Len) {
    char_t *p = Buf;
    bool_t err = FALSE;

    if((pBuf == NULL) || (pBuf->start == NULL) || (Buf == NULL)) {
        return(0);
    }

    while(Len > 0) {
        *p = bufRev(pBuf, &err);
        if(err == TRUE) {
            p++;
        } else {
            *p = '\0';
            return(p - Buf);
        }
        Len--;
    }

    return(p - Buf);
}



/**
 * bufClear - Clean the shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @return:
 *
 * @notes:
 */
void bufClear(buf_t *pBuf) {
    if((pBuf == NULL) || (pBuf->start == NULL)) {
        return;
    }

    pBuf->used = 0;
    pBuf->outpos = pBuf->start;
    pBuf->inpos = pBuf->start;
    do {
        *pBuf->inpos = 0;
        pBuf->inpos++;
    } while(pBuf->inpos != pBuf->end + 1);
    pBuf->inpos = pBuf->start;
}



/**
 * bufGetUsed - Get current used space of the shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @return: Used space.
 *
 * @notes:
 */
word_t bufGetUsed(const buf_t *pBuf) {
    if((pBuf == NULL) || (pBuf->start == NULL)) {
        return(-1);
    }

    return(pBuf->used);
}



/**
 * bufDel - Delete a shared buffer.
 * @pBuf: Poiter to the shared buffer.
 * @return:
 *
 * @notes: It is only free its memory space.
 *         It makes sure that the buffer should not be used
 *         after the buffer deleted.
 */
void bufDel(buf_t *pBuf) {
    if((pBuf != NULL) && (pBuf->start != NULL)) {
        kfree(pBuf->start);
        pBuf->start = NULL;
        kfree(pBuf);
    }
}

